﻿' ******************************************************************************
' ** 
' **  Yahoo Finance Managed
' **  Written by Marius Häusler 2010
' **  It would be pleasant, if you contact me when you are using this code.
' **  Contact: YahooFinanceManaged@gmail.com
' **  Project Home: http://code.google.com/p/yahoo-finance-managed/
' **  
' ******************************************************************************
' **  
' **  Copyright 2010 Marius Häusler
' **  
' **  Licensed under the Apache License, Version 2.0 (the "License");
' **  you may not use this file except in compliance with the License.
' **  You may obtain a copy of the License at
' **  
' **    http://www.apache.org/licenses/LICENSE-2.0
' **  
' **  Unless required by applicable law or agreed to in writing, software
' **  distributed under the License is distributed on an "AS IS" BASIS,
' **  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
' **  See the License for the specific language governing permissions and
' **  limitations under the License.
' ** 
' ******************************************************************************
'Imports System.Windows.Forms


Namespace Base

    Friend Class TimeoutWebClient
        Implements IDisposable

        Private Delegate Sub AsyncCompletedDelegate(ByVal source As IO.MemoryStream, ByVal exception As Net.WebException, ByVal timeout As Integer, ByVal startTime As DateTime, ByVal args As Object)
        Private Delegate Sub AsyncFailureDelegate(ByVal ex As Exception, ByVal args As Object)

        Friend Event AsyncDownloadCompleted(ByVal sender As TimeoutWebClient, ByVal ea As StreamDownloadCompletedEventArgs)

        Private ReadOnly mHelper As New MyHelper
        Private WithEvents mBGW As New BackgroundWorker

        Private mTimeout As Integer = 30000
        Private mProxy As IWebProxy = WebProxy.GetDefaultProxy

        Private mActualDownload As HttpWebRequest = Nothing
        Private mUserArgs As Object = Nothing


        Friend ReadOnly Property AsyncArgs() As Object
            Get
                Return mUserArgs
            End Get
        End Property
        Friend Property Timeout() As Integer
            Get
                Return mTimeout
            End Get
            Set(ByVal value As Integer)
                mTimeout = value
            End Set
        End Property
        Friend Property Proxy() As IWebProxy
            Get
                Return mProxy
            End Get
            Set(ByVal value As IWebProxy)
                mProxy = value
            End Set
        End Property
        Friend ReadOnly Property IsBusy() As Boolean
            Get
                Return mActualDownload IsNot Nothing Or mBGW.IsBusy
            End Get
        End Property

        Friend Sub New(Optional ByVal timeout As Integer = 30000)
            mTimeout = timeout
        End Sub

        Friend Function Download(ByVal url As String) As StreamResponse
            Dim startTime As DateTime = Date.Now
            Dim memStream As IO.MemoryStream = Nothing
            Dim dlException As Net.WebException = Nothing
            Dim size As Integer = 0

            Try
                Me.CancelAsync()

                Dim wr As HttpWebRequest = DirectCast(HttpWebRequest.Create(url), HttpWebRequest)
                mActualDownload = wr
                If mProxy IsNot Nothing Then wr.Proxy = mProxy
                wr.Method = "GET"
                wr.Timeout = mTimeout
                Using resp As HttpWebResponse = DirectCast(wr.GetResponse(), HttpWebResponse)
                    If resp.StatusCode = HttpStatusCode.OK Then
                        Dim s As IO.Stream = resp.GetResponseStream()
                        memStream = Me.CopyToMemoryStream(s)
                        mHelper.CloseStream(s)
                    Else
                        dlException = New Net.WebException("Response status code is not OK(200).", Nothing, Net.WebExceptionStatus.UnknownError, resp)
                    End If
                End Using

            Catch ex As Exception

                If TypeOf ex Is Net.WebException Then
                    dlException = DirectCast(ex, Net.WebException)
                Else
                    dlException = New Net.WebException("A non-Net.WebException was throwed during download process. The unknown Exception is stored as InnerException.", ex, Net.WebExceptionStatus.UnknownError, Nothing)
                End If

            Finally

                If memStream IsNot Nothing AndAlso memStream.CanSeek Then Integer.TryParse(memStream.Length.ToString, size)
                mActualDownload = Nothing
                mUserArgs = Nothing

            End Try

            Return New StreamResponse(New ConnectionInfo(dlException, mTimeout, size, startTime), memStream)
        End Function
        Friend Sub DownloadAsync(ByVal url As String, ByVal args As Object)
            mUserArgs = args
            mBGW.RunWorkerAsync(New Object() {url, args})
        End Sub

        Private Sub mBGW_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles mBGW.DoWork
            Dim asyncArgs As Object() = CType(e.Argument, Object())
            Dim url As String = asyncArgs(0).ToString
            Dim resp As StreamResponse = Me.Download(url)
            e.Result = New Object() {resp, asyncArgs(1)}
        End Sub
        Private Sub mBGW_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles mBGW.RunWorkerCompleted
            Dim asyncArgs As Object() = CType(e.Result, Object())
            Dim args As New StreamDownloadCompletedEventArgs(asyncArgs(1), CType(asyncArgs(0), StreamResponse))
            RaiseEvent AsyncDownloadCompleted(Me, args)
        End Sub


        Friend Sub CancelAsync()
            If mActualDownload IsNot Nothing Then
                mActualDownload.Abort()
                mActualDownload = Nothing
            End If
        End Sub


        Private Function CopyToMemoryStream(ByVal source As IO.Stream) As IO.MemoryStream
            Dim ms As New IO.MemoryStream
            If source.CanRead Then
                Dim buffer As Byte() = New Byte(32767) {}
                While True
                    Dim read As Integer = source.Read(buffer, 0, buffer.Length)
                    If read <= 0 Then
                        Exit While
                    End If
                    ms.Write(buffer, 0, read)
                End While
            End If
            ms.Position = 0
            Return ms
        End Function

#Region "IDisposable"
        Private mDisposedValue As Boolean = False
        Protected Overridable Sub Dispose(ByVal disposing As Boolean)
            If Not mDisposedValue Then
                If disposing Then
                End If
                mBGW.Dispose()
                mBGW = Nothing
                Me.CancelAsync()
                mProxy = Nothing
                mTimeout = 0
                mUserArgs = Nothing
                mActualDownload = Nothing
            End If
            mDisposedValue = True
        End Sub
        Public Sub Dispose() Implements IDisposable.Dispose
            Dispose(True)
            GC.SuppressFinalize(Me)
        End Sub
#End Region

        Private Class AsyncDownloadArgs
            Inherits DownloadEventArgs
            Public StartTime As DateTime
            Public WR As HttpWebRequest
            Public Sub New(ByVal userArgs As Object)
                MyBase.New(userArgs)
            End Sub
        End Class
    End Class


End Namespace
